#pragma once
#include <sqlite3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "mq_logger.hpp"
#include <sys/stat.h>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#ifndef __M_HELPER_H__
#define __MHELPER_H__
namespace bitmq
{
    class Sqlite
    {
        typedef int (*CallBack)(void *, int, char **, char **);

    public:
        Sqlite(std::string file) : _file(file), _header(nullptr) {}
        bool open(int flags = SQLITE_OPEN_FULLMUTEX)
        {
            int ret = sqlite3_open_v2(_file.c_str(), &_header, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | flags, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("打开文件失败");
                return false;
            }
            return true;
        }
        bool exec(const std::string &sql, CallBack cb, void *arg)
        {
            int ret = sqlite3_exec(_header, sql.c_str(), cb, arg, nullptr);
            if (ret != SQLITE_OK)
            {
                ELOG("%s 执行语句失败 %s", sql.c_str(),sqlite3_errmsg(_header));
                return false;
            }
            return true;
        }
        void close()
        {
            if (_header)
                sqlite3_close(_header);
        }

    private:
        sqlite3 *_header;
        std::string _file; // 数据库文件
    };
    class StrHelper
    {
    public:
        static size_t split(std::string str, std::string sep, std::vector<std::string> &result)
        {
            // 字符串分割思想:从0下标的位置开始查找指定字符，进行切割
            // 再重找到的指定字符下标的位置，继续往后找
            // news.music.pop.#
            size_t pos, idx = 0;
            while (idx < str.size())
            {
                pos = str.find(sep, idx);
                if (pos == std::string::npos)
                {
                    result.push_back(str.substr(idx)); // 已经找不到sep所以要把最后的字符串加上
                    return result.size();
                }
                // pos=idx说明有连续的分割符
                if (pos == idx)
                {
                    idx = pos + sep.size();
                    continue;
                }
                result.push_back(str.substr(idx, pos - idx));
                idx = pos + sep.size();
            }
            return result.size();
        }
    };
    class UUIDHelper
    {
    public:
        static std::string uuid()
        {
            // 一:生成8个0~255的数据
            // 1、先生成一个机器随机数，作为种子
            std::random_device rd;

            // 2、再生成伪随机数
            std::mt19937_64 gernator(rd());

            // 3、最后生成0~255的数据
            std::uniform_int_distribution<int> uid(0, 255);

            std::stringstream ss;
            for (int i = 0; i < 8; i++)
            {
                ss << std::setw(2) << std::setfill('0') << std::hex << uid(gernator);
                if (i == 3 || i == 5 || i == 7)
                    ss << "-";
            }

            static std::atomic<size_t> ato(1); // 定义一个原子类型整数初始化为一
            size_t num = ato.fetch_add(1);

            for (int i = 7; i >= 0; i--)
            {
                ss << std::setw(2) << std::setfill('0') << ((num >> (i * 8)) & 0xff);
                if (6 == i)
                    ss << "-";
            }
            return ss.str();
        }
    };
    class FileHelper
    {
    public:
        FileHelper(std::string filename) : _filename(filename) {}
        ~FileHelper() {}
        bool exits() // 文件是否存在
        {
            struct stat st;
            return stat(_filename.c_str(), &st) == 0; // 获取文件属性成功返回0，获取失败返回-1
        }
        size_t size() // 获取文件大小
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                return 0;
            }
            return st.st_size;
        }
        bool read( char *body, size_t offset, size_t len)
        {
            // 1、打开文件
            std::ifstream ifs(_filename, std::ios::binary | std::ios::in);
            if (ifs.is_open() == false)
            {
                ELOG("%s 打开文件失败", _filename.c_str());
                return false;
            }
            // 2、跳转文件读写位置
            ifs.seekg(offset, std::ios::beg); // 从开始位置跳转到offset
            // 3、读文件

            ifs.read(body, len);
            if (ifs.good() == false)
            {
                ELOG("%s 读取文件失败", _filename.c_str());
                ifs.close();
                return false;
            }
            // 4、关闭文件
            ifs.close();
            return true;
        }
        bool read(std::string &body) // 读文件
        {
            // 获取文件大小，根据文件大小，调整body空间
            size_t fsize = this->size();
            body.resize(fsize);
            return read(&body[0], 0, fsize);
        }
        bool write(const char *body, size_t offset, size_t len)
        {
            // 1、打开文件
            std::fstream fs(_filename, std::ios::binary|std::ios::in | std::ios::out);
            if (fs.is_open() == false)
            {
                ELOG("%s 打开文件失败", _filename.c_str());
                return false;
            }
            // 2、跳转位置
            fs.seekp(offset, std::ios::beg);
            // 3、写入文件
            fs.write(body, len);
            if (fs.good() == false)
            {
                ELOG("%s 文件写入失败", _filename.c_str());
                fs.close();
                return false;
            }
            // 4、关闭文件
            fs.close();
            return true;
        }
        bool write(const std::string &body) // 写文件
        {
            return write(body.c_str(),0,body.size());
        }
        bool rename(const std::string&newfilename)
        {
            return (::rename(_filename.c_str(),newfilename.c_str())==0);
        }
        static bool createFile(const std::string&filename) // 创建文件
        {
            std::fstream fs(filename,std::ios::binary|std::ios::out);
            if(fs.is_open()==false)
            {
                ELOG("%s 打开文件失败",filename.c_str());
                return false;
            }
            fs.close();
            return true;
        }
        static bool removeFile(const std::string&filename) // 删除文件
        {
            return (::remove(filename.c_str())==0);
        }
        static std::string parentDirectory(std::string filename) // 获取父级目录
        {
            //aa/bb/cc/test.txt
            size_t pos=filename.find_last_of("/");
            if(pos==std::string::npos)
            {
                //test.txt
                return "./";
            }
            std::string path=filename.substr(0,pos);
            return path;
        }
       static bool createDirectory(const std::string&path) // 创建目录
        {
            //aa/bb/cc
            size_t pos,idx=0;
            while(idx<path.size())
            {
                pos=path.find("/",idx);
                if(pos==std::string::npos)//没找到"/"
                {
                    return (mkdir(path.c_str(),0775)==0);
                }
                std::string subpath=path.substr(0,pos);//我们要一个一个目录创建从 aa -> aa/bb ->aa/bb/cc
                int ret=mkdir(subpath.c_str(),0775);
                if(ret!=0&&errno!=EEXIST)//EEXIST被设置说明文件是存在而创建失败的，我们要忽略这个问题
                {
                    ELOG("%s 创建目录失败",subpath.c_str());
                    return false;
                }
                idx=pos+1;
            }
            return true;
        }
        static bool removeDirectory(const std::string&path) // 删除目录
        {
            std::string com="rm -rf "+path;
            return (system(com.c_str())!=-1);//失败返回-1，成功返回其他
        }

        

    private:
        std::string _filename;
    };
}

#endif